import { ACESFilmicToneMapping, Clock, Color, Fog, FogExp2, IcosahedronGeometry, Layers, LinearSRGBColorSpace, Mesh, MeshBasicMaterial, SRGBColorSpace, ShaderMaterial, Vector2 } from "three";
import { App } from "../application";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass";
import { DotScreenPass } from "three/examples/jsm/postprocessing/DotScreenPass";
import { FilmPass } from "three/examples/jsm/postprocessing/FilmPass";
import { BokehPass } from "three/examples/jsm/postprocessing/BokehPass";

import { RenderPixelatedPass } from "three/examples/jsm/postprocessing/RenderPixelatedPass";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass";
import { SMAAPass } from "three/examples/jsm/postprocessing/SMAAPass";
import { HorizontalBlurShader } from "three/examples/jsm/shaders/HorizontalBlurShader";
import { VerticalBlurShader } from "three/examples/jsm/shaders/VerticalBlurShader";
import { SharpenShader } from "../renderExtension/Material/shaders/SharpenShader";
import { RainShader } from "../renderExtension/Material/shaders/RainShader"
import { SnowShader } from "../renderExtension/Material/shaders/SnowShader";
// import { OutputPass } from "three/examples/jsm/postprocessing/OutputPass"
import { Point } from "../Engine";
import Const from "../application/constants/Const";
import { OutputPass } from "three/examples/jsm/postprocessing/OutputPass";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader";




class PostProcess {
    private app: App;
    public effectComposer: EffectComposer;
    /**
     * 可选边线发光效果生效时启用这个composer
     */
    public bloomComposer: EffectComposer;
    public finalComposer: EffectComposer;
    public openSelectiveBloom: boolean = false;

    public outlinePass;
    public bloomPass;
    public dotScreenPass;
    public filmPass;
    public bokehPass;
    public pixelPass;
    public effectHBlur;
    public effectVBlur;
    public sharpenEffect;
    public rainEffect;
    public snowEffect;
    public smaaPass;
    public fog;
    private bloomLayer: Layers = new Layers();
    /**
     * 是否启用着色通道，默认不开启
     */
    public enable = false;
    constructor() {
        this.app = App.getInstance();
        this.initEffectComposer();
        this.initPostProcess();
    }

    /**
     * 开启局部边线发光的情况下的render函数
     */
    public renderUnrealBloom() {
        let materials = [];
        let scope = this;
        const darkMaterial = new MeshBasicMaterial({ color: 'black' });
        this.app.three.scene.traverse(function (obj) {
            if (obj['isMesh'] && scope.bloomLayer.test(obj.layers) === false) {
                materials[obj.uuid] = (<Mesh>obj).material;
                (<Mesh>obj).material = darkMaterial;
            }
            else if (obj['isMesh']) {
                let a = obj;
                // console.log(obj.parent.parent['options'].url)
            }
        });
        this.bloomComposer.render();
        this.app.three.scene.traverse(function (obj) {
            if (materials[obj.uuid]) {
                (<Mesh>obj).material = materials[obj.uuid];
                delete materials[obj.uuid];
            }
        });

        // render the entire scene, then render bloom scene on top
        this.finalComposer.render();
    }

    
    /**
     * 开启局部边线发光
     * @param options 可选参数，该参数中的字段含义如下： threshold:阈值（0-1）; strength:发光强度（0-1）; radius:发光发散范围(0-infinity); 
     * @example  app.postProcess.openSelectiveUnrealBloomPass({
        threshold: 0.5,
        strength: 0.5,
        radius: 0.5,
    })
     */
    public openSelectiveUnrealBloomPass(options) {
        //关闭其余着色通道
        this.enable = false;
        this.openSelectiveBloom = true;
        options = options ? options : {};

        const BLOOM_SCENE = 1;
        this.bloomLayer.set(BLOOM_SCENE);
        //初始化独立的着色通道
        const renderScene = new RenderPass(this.app.three.scene, this.app.three.camera);

        const bloomPass = new UnrealBloomPass(new Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);

        bloomPass.threshold = (!(options.threshold === undefined) ? options.threshold : 0);
        bloomPass.strength = (!(options.strength === undefined) ? options.strength : 1);
        bloomPass.radius = (!(options.radius === undefined) ? options.radius : 0.5);
        // this.app.three.renderer.toneMappingExposure = 1.0;

        this.bloomComposer = new EffectComposer(this.app.three.renderer);
        let div = document.getElementById(App.getInstance().div);
        this.bloomComposer.setPixelRatio(window.devicePixelRatio);
        this.bloomComposer.setSize(div.offsetWidth, div.offsetHeight);

        //离屏渲染
        this.bloomComposer.renderToScreen = false;
        this.bloomComposer.addPass(renderScene);
        this.bloomComposer.addPass(bloomPass);

        let scope = this;
        let shaderMaterial = new ShaderMaterial({
            uniforms: {
                baseTexture: { value: null },
                bloomTexture: { value: scope.bloomComposer.renderTarget2.texture }
            },
            vertexShader: `varying vec2 vUv;

             void main() {
 
                 vUv = uv;
 
                 gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
 
             }`,
            fragmentShader: `uniform sampler2D baseTexture;
             uniform sampler2D bloomTexture;
 
             varying vec2 vUv;
 
             void main() {
 
                 gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
 
             }
             `,
            defines: {}
        })
        const finalPass = new ShaderPass(
            shaderMaterial, 'baseTexture'
        );
        finalPass.needsSwap = true;

        const outputPass = new OutputPass();

        this.finalComposer = new EffectComposer(this.app.three.renderer);
        this.finalComposer.addPass(renderScene);
        let FXAAPass = new ShaderPass(FXAAShader);
        FXAAPass.uniforms['resolution'].value.set(1 / this.app.getSize().width, 1 / this.app.getSize().height);
        this.finalComposer.addPass(FXAAPass);

        this.finalComposer.addPass(finalPass);
        this.finalComposer.addPass(outputPass);

        // this.test();
        this.renderUnrealBloom();
    }

    private test() {
        const geometry = new IcosahedronGeometry(1, 15);

        let p = new Point(118.7270256509128, 32.00248268414715, 20, Const.EPSGType.EPSG84).toEPSGWeb();
        for (let i = 0; i < 50; i++) {

            const color = new Color(1, 0, 0);
            color.setHSL(Math.random(), 0.7, Math.random() * 0.2 + 0.05);

            const material = new MeshBasicMaterial({ color: color });
            const sphere = new Mesh(geometry, material);
            sphere.position.x = p.x + Math.random() * 100 - 5;
            sphere.position.y = p.y + Math.random() * 100 - 5;
            sphere.position.z = p.z + Math.random() * 100 - 5;
            // sphere.position.normalize().multiplyScalar(Math.random() * 4.0 + 2.0);
            sphere.scale.setScalar(Math.random() * Math.random() * 100 + 0.5);
            this.app.three.scene.add(sphere);

            if (Math.random() < 0.25)
                sphere.layers.enable(1);
        }
    }

    /**
     * 关闭局部边线发光通道，开启全局着色通道
     */
    public closeSelectiveUnrealBloomPass() {
        this.enable = true;
        this.openSelectiveBloom = false;
    }
    /**
     * 边线发光
     * @param visible 是否启用
     * @param options 可选参数，该参数中的字段含义如下： threshold:阈值（0-1）; strength:发光强度（0-1）; radius:发光发散范围(0-infinity); 
     * @example  app.postProcess.setUnrealBloomPass(visible, {
        threshold: 0.5,
        strength: 0.5,
        radius: 0.5,
    })
     * @returns: 
     */
    public setUnrealBloomPass(visible = false, options: { threshold?: number, strength?: number, radius?: number, exposure?: number }) {
        this.enable = true;

        if (this.bloomComposer) {
            this.bloomComposer.dispose();
            this.bloomComposer = null;
        }
        this.bloomPass.enabled = visible;

        this.bloomPass.threshold = (!(options.threshold === undefined) ? options.threshold : this.bloomPass.threshold);
        this.bloomPass.strength = (!(options.strength === undefined) ? options.strength : this.bloomPass.strength);
        this.bloomPass.radius = (!(options.radius === undefined) ? options.radius : this.bloomPass.radius);
        // postprocess.bloomPass.highPassUniforms['luminosityThreshold'].value = (!(options.threshold === undefined) ? options.threshold : postprocess.bloomPass.highPassUniforms['luminosityThreshold'].value);
        // postprocess.bloomPass.compositeMaterial.uniforms['bloomStrength'].value = (!(options.strength === undefined) ? options.strength : postprocess.bloomPass.compositeMaterial.uniforms['bloomStrength'].value);
        // postprocess.bloomPass.compositeMaterial.uniforms['bloomRadius'].value = (!(options.radius === undefined) ? options.radius : postprocess.bloomPass.compositeMaterial.uniforms['bloomRadius'].value);
        // this.app.three.renderer.toneMappingExposure = (!(options.exposure === undefined) ? options.exposure : this.app.three.renderer.toneMappingExposure)
    }

    /**
     * 设置胶片颗粒通道
     * @param visible 是否启用 
     * @param options noiseIntensity：噪声强度(0-1),grayscale: 是否启用灰度模式
     * @example   app.postProcess.setFilmPass(true, {
        noiseIntensity: 0.5,
        grayscale: false,
    })
     * @returns 
     */
    public setFilmPass(visible = false, options: { noiseIntensity?: number, scanlinesIntensity?: number, scanlinesCount?: number, grayscale?: boolean }) {
        this.enable = true;
        this.filmPass.enabled = visible;
        this.filmPass.uniforms['intensity'].value = (!(options.noiseIntensity === undefined) ? options.noiseIntensity : this.filmPass.uniforms['intensity'].value);
        //其余参数依然生效
        // this.filmPass.uniforms['sIntensity'].value = (!(options.scanlinesIntensity === undefined) ? options.scanlinesIntensity : this.filmPass.uniforms['sIntensity'].value);
        // this.filmPass.uniforms['sCount'].value = (!(options.scanlinesCount === undefined) ? options.scanlinesCount : this.filmPass.uniforms['sCount'].value);
        this.filmPass.uniforms['grayscale'].value = (!(options.grayscale === undefined) ? options.grayscale : this.filmPass.uniforms['grayscale'].value);
    }

    /**
     * 设置景深通道
     * @param visible 是否启用景深
     * @param options aspect:虚化强度(1-10);aperture 景深范围(0-1)
     * @example   app.postProcess.setBokehPass(true, {
        focus: 1.0,
        aperture: 0.025,
    })
     * @returns 
     */
    public setBokehPass(visible = false, options: { aspect?: number, aperture?: number, focus?: number, maxblur?: number }) {
        this.enable = true;
        this.bokehPass.enabled = visible;
        this.bokehPass.uniforms['focus'].value = options.focus ? options.focus : this.bokehPass.uniforms['focus'].value;
        this.bokehPass.uniforms['aspect'].value = options.aspect ? options.aspect : this.bokehPass.uniforms['aspect'].value;
        this.bokehPass.uniforms['aperture'].value = 0.00000001 + 0.000025 * (!(options.aperture === undefined) ? (1 - options.aperture) : 0);
        this.bokehPass.uniforms['maxblur'].value = options.maxblur ? options.maxblur : this.bokehPass.uniforms['maxblur'].value;
    }

    /**
     * 马赛克
     * @param visible 
     * @param options 
     */
    public setPixelPass(visible = false, options: {  pixel?: number }) {
        this.enable = true;
        this.pixelPass.enabled = visible;
        this.pixelPass.setPixelSize( options.pixel );
        // this.pixelPass.uniforms['focus'].value = options.focus ? options.focus : this.pixelPass.uniforms['focus'].value;
        // this.pixelPass.uniforms['aspect'].value = options.aspect ? options.aspect : this.pixelPass.uniforms['aspect'].value;
        // this.pixelPass.uniforms['aperture'].value = 0.00000001 + 0.000025 * (!(options.aperture === undefined) ? (1 - options.aperture) : 0);
        // this.pixelPass.uniforms['maxblur'].value = options.maxblur ? options.maxblur : this.pixelPass.uniforms['maxblur'].value;
    }

    /**
     * 设置边角模糊
     * @param visible 是否启用边角模糊
     * @param options range:模糊范围(0-1); intensity 模糊强度(0-1)
     * @example app.postProcess.setBlurPass(true, {
         range: 0.8,
         intensity: 0.1
     })
     * @returns 
     */
    public setBlurPass(visible = false, options: { range?: number, intensity?: number }) {
        this.enable = true;
        this.effectHBlur.enabled = visible;
        this.effectVBlur.enabled = visible;


        const width = window.innerWidth * (1 - options.range) || 2;
        const height = window.innerHeight * (1 - options.range) || 2;
        this.effectHBlur.uniforms['h'].value = (2 / (width / 2)) * (!(options.intensity === undefined) ? options.intensity : 1.0);
        this.effectVBlur.uniforms['v'].value = (2 / (height / 2)) * (!(options.intensity === undefined) ? options.intensity : 1.0);

        //'tDiffuse': { value: null },

        return true;
    }

    /**
     * 设置锐化
     * @param visible 锐化是否可用
     * @param options sharpness:锐化度(0-infinity)
     * @example  app.postProcess.setSharpenPass(visible, {
        sharpness: 0.2,
    })
     */
    public setSharpenPass(visible = false, options: { sharpness?: number }) {
        this.enable = true;
        this.sharpenEffect.enabled = visible;
        this.sharpenEffect.uniforms.sharpness.value = (!(options.sharpness === undefined) ? options.sharpness : 0.5)
    }

    public setRainPass(visible = false, options: { clockNumber?: number }) {
        
        this.enable = true;
        this.rainEffect.enabled = visible;
      
      this.rainEffect.uniforms['iTime'].value += options.clockNumber;
      
    }

    public setSnowPass(visible = false, options: { clockNumber?: number }) {
        
        this.enable = true;
        this.snowEffect.enabled = visible;
      
      this.snowEffect.uniforms['time'].value += options.clockNumber;
       
    }


    /**
     * 设置SMAA抗锯齿
     * @param visible 是否启用抗锯齿
     */
    public setSMAAPass(visible = false) {
        this.enable = true;
        this.smaaPass.enabled = visible;
    }


    /**
     * 添加外边框线
     * @param color：边框线的颜色
     * @returns：获取后处理对象
    */
    private initOutlinePass(color: string) {
        let size = this.app.getSize();

        let outlinePass = new OutlinePass(new Vector2(size.width, size.height), this.app.three.scene, this.app.three.camera, []);
        outlinePass.visibleEdgeColor = new Color(color ? color : "#FF0000");
        this.effectComposer.addPass(outlinePass);
        outlinePass.enabled = false;
        this.outlinePass = outlinePass;

        return outlinePass;
    }


    /**
     * 边线发光
     * @param options 可选参数，该参数中的字段含义如下： threshold:阈值（默认0.5）; Strength:发光强度（默认0.5）; radius:发光发散范围（默认0.0）; exposure:曝光程度（默认0.5）
     * @returns: 后处理对象
     */
    private initUnrealBloomPass(options: any = {}) {
        let size = this.app.getSize();

        let bloomPass = new UnrealBloomPass(new Vector2(window.innerWidth, window.innerHeight), options.strength || 0.21, options.radius || 0.55, options.threshold || 0.21);
        bloomPass.renderToScreen = true;
        // bloomPass.threshold = options.threshold || 0.21;
        // bloomPass.strength = options.strength || 1.2;
        // bloomPass.radius = options.radius || 0.55;
        // this.app.three.renderer.toneMappingExposure = options.exposure || 1.0;
        this.effectComposer.addPass(bloomPass);

        bloomPass.enabled = false;
        this.bloomPass = bloomPass;
        return bloomPass;
    }

    /**
     * 添加纱窗透视效果
     * @param options 
     * @returns 
     */
    private initDotScreenPass(options: any = {}) {
        let dotScreenPass = new DotScreenPass(options.center, options.angle, options.scale);
        this.effectComposer.addPass(dotScreenPass);

        dotScreenPass.enabled = false;
        this.dotScreenPass = dotScreenPass;
        return dotScreenPass;
    }

    /**
     * 添加胶片颗粒通道
     * @param options  noiseIntensity：噪声强度(0-1)
     * @returns 
     */
    private initFilmPass(options: any = {}) {
        let filmPass = new FilmPass(options.noiseIntensity, options.grayscale);
        this.effectComposer.addPass(filmPass);

        filmPass.enabled = false;
        this.filmPass = filmPass;
        return filmPass;
    }

    /**
     * 添加景深通道
     * @param options aspect:虚化强度(1-10);aperture 景深范围(0-1)
     * @returns 
     */
    private initBokehPass(options: any = {}) {
        let bokenPass = new BokehPass(this.app.three.scene, this.app.three.camera, options);
        this.effectComposer.addPass(bokenPass);

        bokenPass.enabled = false;
        this.bokehPass = bokenPass;
        return bokenPass;
    }

        /**
     * 添加马赛克
     * @param options aspect:虚化强度(1-10);aperture 景深范围(0-1)
     * @returns 
     */
         private initPixel(options: any = {}) {
            let pixelPass = new RenderPixelatedPass( 16, this.app.three.scene, this.app.three.camera, );
            // let bokenPass = new BokehPass(this.app.three.scene, this.app.three.camera, options);
            this.effectComposer.addPass(pixelPass);
            // composer.addPass(  );
    
            pixelPass.enabled = false;
            this.pixelPass = pixelPass;
            return pixelPass;
        }
    /**
     * 添加边角模糊
     * @param options  range:模糊范围(0-1); intensity 模糊强度(0-1)
     * @returns 
     */
    private initBlurPass(options: any = {}) {
        const width = window.innerWidth || 2;
        const height = window.innerHeight || 2;
        let effectHBlur = new ShaderPass(HorizontalBlurShader);
        let effectVBlur = new ShaderPass(VerticalBlurShader);
        let value = options.value ? options.value : 1;
        let diffuse = options.diffuse ? options.diffuse : 1;

        effectHBlur.uniforms['h'].value = (2 / (width / 2)) * value;
        effectVBlur.uniforms['v'].value = (2 / (height / 2)) * value;
        // effectHBlur.uniforms['tDiffuse'].value = diffuse * 1.0;
        // effectVBlur.uniforms['tDiffuse'].value = diffuse * 1.0;
        //'tDiffuse': { value: null },
        effectHBlur.enabled = false;
        effectVBlur.enabled = false;
        this.effectComposer.addPass(effectHBlur);
        this.effectComposer.addPass(effectVBlur);
        this.effectHBlur = effectHBlur;
        this.effectVBlur = effectVBlur;

        return true;
    }

    private initSharpenPass(options: any = {}) {
        let sharpenEffect = new ShaderPass(SharpenShader);
        sharpenEffect.uniforms.width.value = window.innerWidth;
        sharpenEffect.uniforms.height.value = window.innerHeight;
        sharpenEffect.uniforms.sharpness.value = options.sharpness ? options.sharpness : 1.0;

        sharpenEffect.enabled = false;
        this.effectComposer.addPass(sharpenEffect);

        this.sharpenEffect = sharpenEffect;
        return sharpenEffect;
    }

    private initRainPass(options: any = {}) {
        let rainEffect = new ShaderPass(RainShader);
        rainEffect['PASSID'] = 'rainPass';
        rainEffect.uniforms['iResolution'].value = new Vector2(window.innerWidth, window.innerHeight);
        rainEffect.enabled = false;
        this.effectComposer.addPass(rainEffect);

        this.rainEffect = rainEffect;
        return rainEffect;
    }

    private initSnowPass(options: any = {}) {
        let snowEffect = new ShaderPass(SnowShader);
        snowEffect['PASSID'] = 'snowPass';
        snowEffect.uniforms['iResolution'].value = new Vector2(window.innerWidth, window.innerHeight);
        snowEffect.enabled = false;
        this.effectComposer.addPass(snowEffect);

        this.snowEffect = snowEffect;
        return snowEffect;
    }

    /**
     * 添加SMAA抗锯齿
     * @returns 
     */
    private initSMAAPass() {
        let p = window.innerWidth * this.app.three.renderer.getPixelRatio();
        let q = window.innerHeight * this.app.three.renderer.getPixelRatio();
        //let pass = new SMAAPass(window.innerWidth * view.renderer.getPixelRatio(), window.innerHeight * view.renderer.getPixelRatio());
        let size = this.app.getSize()
        let smaaPass = new SMAAPass();

        smaaPass.enabled = false;
        this.effectComposer.addPass(smaaPass);
        this.smaaPass = smaaPass;
        return smaaPass;
    }

    private initEffectComposer() {
        let size = this.app.getSize();
        if (!this.effectComposer) {
            this.effectComposer = new EffectComposer(this.app.three.renderer);
            this.effectComposer.setSize(size.width, size.height);
            let renderPass = new RenderPass(this.app.three.scene, this.app.three.camera);
            this.effectComposer.addPass(renderPass);
        }
    }

    private initPostProcess() {
        this.initOutlinePass('#FFFFFFF');
        this.initUnrealBloomPass();
        this.initDotScreenPass();
        this.initFilmPass();
        this.initBokehPass();
        this.initPixel();
        this.initBlurPass();
        this.initSharpenPass();
        this.initRainPass();
        this.initSnowPass();
        this.initSMAAPass();

        const outputPass = new OutputPass();
        this.effectComposer.addPass(outputPass);
    }
}
export { PostProcess };